<script lang="ts" setup>
import Language, { languageType } from './language'
import cronstrue from 'cronstrue/i18n'
import { reactive, computed, toRefs, watch } from 'vue'
interface Props {
  // 绑定值
  modelValue: string
  // 当前语言
  i18n?: languageType
  // 最大高度
  maxHeight?: string
  // 显示切换语言按钮
  showChangeLanguage?: boolean
}
const props = withDefaults(defineProps<Props>(), {
  modelValue: '* * * * * ? *',
  i18n: 'cn',
  maxHeight: '400px',
  showChangeLanguage: false,
})
const emit = defineEmits(['save', 'close'])
type valueDTO = 'second' | 'minute' | 'hour' | 'month' | 'year'
const { i18n } = toRefs(props)
// 时/分/秒 值读取getter
const valueGetter = (valType: valueDTO) => {
  let seconds = ''
  const cronEvery = state[valType].cronEvery
  switch (cronEvery.toString()) {
    case '1':
      seconds = '*'
      break
    case '2':
      seconds = state[valType].incrementStart + '/' + state[valType].incrementIncrement
      break
    case '3':
      seconds = state[valType].specificSpecific.join(',')
      break
    case '4':
      seconds = state[valType].rangeStart + '-' + state[valType].rangeEnd
      break
  }
  return seconds
}
// 时/分/秒 值设置setter
const valueSetter = (valType: valueDTO, value: string) => {
  if (value === '*') {
    state[valType].cronEvery = '1'
  } else if (value.indexOf('/') > -1) {
    const indexArr = value.split('/')
    state[valType].incrementStart = Number(indexArr[0])
    state[valType].incrementIncrement = Number(indexArr[1])
    state[valType].cronEvery = '2'
  } else if (value.indexOf('-') > -1) {
    const indexArr = value.split('-')
    state[valType].rangeStart = Number(indexArr[0])
    state[valType].rangeEnd = Number(indexArr[1])
    state[valType].cronEvery = '4'
  } else {
    state[valType].cronEvery = '3'
    state[valType].specificSpecific = value.split(',').map((item) => Number(item))
  }
}
// 天 值设置
const daysValueSetter = (value: string) => {
  if (value === '*') {
    state.day.cronEvery = '1'
  } else if (value.indexOf('/') > -1) {
    const indexArr = value.split('/')
    state.day.incrementStart = Number(indexArr[0])
    state.day.incrementIncrement = Number(indexArr[1])
    state.day.cronEvery = '3'
  } else if (value === 'L') {
    state.day.cronEvery = '6'
  } else if (value === 'LW') {
    state.day.cronEvery = '7'
  } else if (value.endsWith('L')) {
    const indexArr = value.split('L')
    state.day.cronLastSpecificDomDay = Number(indexArr[0])
    state.day.cronEvery = '8'
  } else if (value.startsWith('L-')) {
    const indexArr = value.split('L-')
    state.day.cronDaysBeforeEomMinus = Number(indexArr[1])
    state.day.cronEvery = '9'
  } else if (value.endsWith('W')) {
    const indexArr = value.split('W')
    state.day.cronDaysNearestWeekday = Number(indexArr[0])
    state.day.cronEvery = '10'
  } else if (value === '?') {
    // state.day.cronEvery = '11'
  } else {
    state.day.specificSpecific = value.split(',').map((item) => Number(item))
    state.day.cronEvery = '5'
  }
}
// 星期 值设置
const weeksValueSetter = (value: string) => {
  if (value === '?') {
    // state.day.cronEvery = '5'
  } else if (value.indexOf('/') > -1) {
    const indexArr = value.split('/')
    state.week.incrementStart = Number(indexArr[0])
    state.week.incrementIncrement = Number(indexArr[1])
    state.day.cronEvery = '2'
  } else if (value.indexOf('#') > -1) {
    const indexArr = value.split('#')
    state.week.cronNthDayDay = Number(indexArr[0])
    state.week.cronNthDayNth = Number(indexArr[1])
    state.day.cronEvery = '11'
  } else {
    state.week.specificSpecific = value.split(',').map((item) => item)
    state.day.cronEvery = '4'
  }
}
const state: any = reactive({
  language: i18n.value,
  second: {
    cronEvery: '1',
    incrementStart: 3,
    incrementIncrement: 5,
    rangeStart: 0,
    rangeEnd: 0,
    specificSpecific: [],
  },
  minute: {
    cronEvery: '1',
    incrementStart: 3,
    incrementIncrement: 5,
    rangeStart: 0,
    rangeEnd: 0,
    specificSpecific: [],
  },
  hour: {
    cronEvery: '1',
    incrementStart: 3,
    incrementIncrement: 5,
    rangeStart: 0,
    rangeEnd: 0,
    specificSpecific: [],
  },
  day: {
    cronEvery: '1',
    incrementStart: 1,
    incrementIncrement: 1,
    rangeStart: 0,
    rangeEnd: 0,
    specificSpecific: [],
    cronLastSpecificDomDay: 1,
    cronDaysBeforeEomMinus: 0,
    cronDaysNearestWeekday: 0,
  },
  week: {
    cronEvery: '1',
    incrementStart: 1,
    incrementIncrement: 1,
    specificSpecific: [],
    cronNthDayDay: 1,
    cronNthDayNth: 1,
  },
  month: {
    cronEvery: '1',
    incrementStart: 3,
    incrementIncrement: 5,
    rangeStart: 0,
    rangeEnd: 0,
    specificSpecific: [],
  },
  year: {
    cronEvery: '1',
    incrementStart: 2017,
    incrementIncrement: 1,
    rangeStart: 0,
    rangeEnd: 0,
    specificSpecific: [],
  },
  output: {
    second: '',
    minute: '',
    hour: '',
    day: '',
    month: '',
    Week: '',
    year: '',
  },
  text: computed(() => Language[(state.language as languageType) || 'cn']),
  secondsText: computed(() => valueGetter('second')),
  minutesText: computed(() => valueGetter('minute')),
  hoursText: computed(() => valueGetter('hour')),
  daysText: computed(() => {
    let days = ''
    const cronEvery = state.day.cronEvery
    switch (cronEvery.toString()) {
      case '1':
        break
      case '2':
      case '4':
      case '11':
        days = '?'
        break
      case '3':
        days = state.day.incrementStart + '/' + state.day.incrementIncrement
        break
      case '5':
        days = state.day.specificSpecific.join(',')
        break
      case '6':
        days = 'L'
        break
      case '7':
        days = 'LW'
        break
      case '8':
        days = state.day.cronLastSpecificDomDay + 'L'
        break
      case '9':
        days = 'L-' + state.day.cronDaysBeforeEomMinus
        break
      case '10':
        days = state.day.cronDaysNearestWeekday + 'W'
        break
    }
    return days
  }),
  weeksText: computed(() => {
    let weeks = ''
    const cronEvery = state.day.cronEvery
    switch (cronEvery.toString()) {
      case '1':
      case '3':
      case '5':
        weeks = '?'
        break
      case '2':
        weeks = state.week.incrementStart + '/' + state.week.incrementIncrement
        break
      case '4':
        weeks = state.week.specificSpecific.join(',')
        break
      case '6':
      case '7':
      case '8':
      case '9':
      case '10':
        weeks = '?'
        break
      case '11':
        weeks = state.week.cronNthDayDay + '#' + state.week.cronNthDayNth
        break
    }
    return weeks
  }),
  monthsText: computed(() => valueGetter('month')),
  yearsText: computed(() => valueGetter('year')),
  cron: computed(() => {
    return `${state.secondsText || '*'} ${state.minutesText || '*'} ${state.hoursText || '*'} ${
      state.daysText || '*'
    } ${state.monthsText || '*'} ${state.weeksText || '?'} ${state.yearsText || '*'}`
  }),
})
// 设置值
const setValue = () => {
  if (!props.modelValue) {
    return
  }
  const textList = props.modelValue.split(' ')
  if (!props.modelValue || typeof props.modelValue !== 'string' || textList.length !== 7) {
    console.warn('请传入合法cron表达式')
    return
  }
  valueSetter('second', textList[0])
  valueSetter('minute', textList[1])
  valueSetter('hour', textList[2])
  daysValueSetter(textList[3])
  valueSetter('month', textList[4])
  weeksValueSetter(textList[5])
  valueSetter('year', textList[6])
}
// 监听绑定值是否变化
watch(
  () => props.modelValue,
  () => {
    setValue()
  },
  {
    immediate: true,
  },
)
const getValue = () => {
  return state.cron
}
// 关闭
const close = () => {
  emit('close')
  // 当前表达式与传入值不同，还原
  getValue() !== props.modelValue && setValue()
}
const cronExplain = computed(() => {
  return cronstrue.toString(getValue(), { locale: 'zh_CN' })
})
const handleChange = () => {
  if (typeof state.cron !== 'string') return false
  emit('save', state.cron, cronExplain.value)
  emit('close')
}
</script>
<template>
  <div class="vue3-cron-div">
    <el-button
      v-if="showChangeLanguage"
      class="language"
      link
      type="primary"
      @click="state.language = state.language === 'en' ? 'cn' : 'en'"
      >{{ state.language === 'en' ? 'cn' : 'en' }}</el-button
    >
    <el-tabs>
      <el-tab-pane>
        <template #label>
          <span>
            {{ state.text.Seconds.name }}
          </span>
        </template>
        <div class="tabBody" :style="{ 'max-height': maxHeight }">
          <el-row>
            <el-radio v-model="state.second.cronEvery" label="1">{{
              state.text.Seconds.every
            }}</el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.second.cronEvery" label="2"
              >{{ state.text.Seconds.interval[0] }}
              <el-input-number
                v-model="state.second.incrementIncrement"
                :min="1"
                :max="60"
              ></el-input-number>
              {{ state.text.Seconds.interval[1] || '' }}
              <el-input-number
                v-model="state.second.incrementStart"
                :min="0"
                :max="59"
              ></el-input-number>
              {{ state.text.Seconds.interval[2] || '' }}
            </el-radio>
          </el-row>
          <el-row>
            <el-radio class="long" v-model="state.second.cronEvery" label="3"
              >{{ state.text.Seconds.specific }}
              <el-select multiple v-model="state.second.specificSpecific">
                <el-option v-for="(val, index) in 60" :key="index" :value="val - 1">{{
                  val - 1
                }}</el-option>
              </el-select>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.second.cronEvery" label="4"
              >{{ state.text.Seconds.cycle[0] }}
              <el-input-number
                v-model="state.second.rangeStart"
                :min="1"
                :max="60"
              ></el-input-number>
              {{ state.text.Seconds.cycle[1] || '' }}
              <el-input-number v-model="state.second.rangeEnd" :min="0" :max="59"></el-input-number>
              {{ state.text.Seconds.cycle[2] || '' }}
            </el-radio>
          </el-row>
        </div>
      </el-tab-pane>
      <el-tab-pane>
        <template #label>
          <span>
            {{ state.text.Minutes.name }}
          </span>
        </template>
        <div class="tabBody" :style="{ 'max-height': maxHeight }">
          <el-row>
            <el-radio v-model="state.minute.cronEvery" label="1">{{
              state.text.Minutes.every
            }}</el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.minute.cronEvery" label="2"
              >{{ state.text.Minutes.interval[0] }}
              <el-input-number
                v-model="state.minute.incrementIncrement"
                :min="1"
                :max="60"
              ></el-input-number>
              {{ state.text.Minutes.interval[1] }}
              <el-input-number
                v-model="state.minute.incrementStart"
                :min="0"
                :max="59"
              ></el-input-number>
              {{ state.text.Minutes.interval[2] || '' }}
            </el-radio>
          </el-row>
          <el-row>
            <el-radio class="long" v-model="state.minute.cronEvery" label="3"
              >{{ state.text.Minutes.specific }}
              <el-select multiple v-model="state.minute.specificSpecific">
                <el-option v-for="(val, index) in 60" :key="index" :value="val - 1">{{
                  val - 1
                }}</el-option>
              </el-select>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.minute.cronEvery" label="4"
              >{{ state.text.Minutes.cycle[0] }}
              <el-input-number
                v-model="state.minute.rangeStart"
                :min="1"
                :max="60"
              ></el-input-number>
              {{ state.text.Minutes.cycle[1] }}
              <el-input-number v-model="state.minute.rangeEnd" :min="0" :max="59"></el-input-number>
              {{ state.text.Minutes.cycle[2] }}
            </el-radio>
          </el-row>
        </div>
      </el-tab-pane>
      <el-tab-pane>
        <template #label>
          <span>
            {{ state.text.Hours.name }}
          </span>
        </template>
        <div class="tabBody" :style="{ 'max-height': maxHeight }">
          <el-row>
            <el-radio v-model="state.hour.cronEvery" label="1">{{
              state.text.Hours.every
            }}</el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.hour.cronEvery" label="2"
              >{{ state.text.Hours.interval[0] }}
              <el-input-number
                v-model="state.hour.incrementIncrement"
                :min="0"
                :max="23"
              ></el-input-number>
              {{ state.text.Hours.interval[1] }}
              <el-input-number
                v-model="state.hour.incrementStart"
                :min="0"
                :max="23"
              ></el-input-number>
              {{ state.text.Hours.interval[2] }}
            </el-radio>
          </el-row>
          <el-row>
            <el-radio class="long" v-model="state.hour.cronEvery" label="3"
              >{{ state.text.Hours.specific }}
              <el-select multiple v-model="state.hour.specificSpecific">
                <el-option v-for="(val, index) in 24" :key="index" :value="val - 1">{{
                  val - 1
                }}</el-option>
              </el-select>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.hour.cronEvery" label="4"
              >{{ state.text.Hours.cycle[0] }}
              <el-input-number v-model="state.hour.rangeStart" :min="0" :max="23"></el-input-number>
              {{ state.text.Hours.cycle[1] }}
              <el-input-number v-model="state.hour.rangeEnd" :min="0" :max="23"></el-input-number>
              {{ state.text.Hours.cycle[2] }}
            </el-radio>
          </el-row>
        </div>
      </el-tab-pane>
      <el-tab-pane>
        <template #label>
          <span>
            {{ state.text.Day.name }}
          </span>
        </template>
        <div class="tabBody" :style="{ 'max-height': maxHeight }">
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="1">{{ state.text.Day.every }}</el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="2"
              >{{ state.text.Day.intervalWeek[0] }}
              <el-input-number
                v-model="state.week.incrementIncrement"
                :min="1"
                :max="7"
              ></el-input-number>
              {{ state.text.Day.intervalWeek[1] }}
              <el-select v-model="state.week.incrementStart">
                <el-option
                  v-for="(val, index) in 7"
                  :key="index"
                  :label="state.text.Week[val - 1]"
                  :value="val"
                ></el-option>
              </el-select>
              {{ state.text.Day.intervalWeek[2] }}
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="3"
              >{{ state.text.Day.intervalDay[0] }}
              <el-input-number
                v-model="state.day.incrementIncrement"
                :min="1"
                :max="31"
              ></el-input-number>
              {{ state.text.Day.intervalDay[1] }}
              <el-input-number
                v-model="state.day.incrementStart"
                :min="1"
                :max="31"
              ></el-input-number>
              {{ state.text.Day.intervalDay[2] }}
            </el-radio>
          </el-row>
          <el-row>
            <el-radio class="long" v-model="state.day.cronEvery" label="4"
              >{{ state.text.Day.specificWeek }}
              <el-select multiple v-model="state.week.specificSpecific">
                <el-option
                  v-for="(val, index) in 7"
                  :key="index"
                  :label="state.text.Week[val - 1]"
                  :value="['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'][val - 1]"
                ></el-option>
              </el-select>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio class="long" v-model="state.day.cronEvery" label="5"
              >{{ state.text.Day.specificDay }}
              <el-select multiple v-model="state.day.specificSpecific">
                <el-option v-for="(val, index) in 31" :key="index" :value="val">{{
                  val
                }}</el-option>
              </el-select>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="6">{{
              state.text.Day.lastDay
            }}</el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="7">{{
              state.text.Day.lastWeekday
            }}</el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="8"
              >{{ state.text.Day.lastWeek[0] }}
              <el-select v-model="state.day.cronLastSpecificDomDay">
                <el-option
                  v-for="(val, index) in 7"
                  :key="index"
                  :label="state.text.Week[val - 1]"
                  :value="val"
                ></el-option>
              </el-select>
              {{ state.text.Day.lastWeek[1] || '' }}
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="9">
              <el-input-number
                v-model="state.day.cronDaysBeforeEomMinus"
                :min="1"
                :max="31"
              ></el-input-number>
              {{ state.text.Day.beforeEndMonth[0] }}
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="10"
              >{{ state.text.Day.nearestWeekday[0] }}
              <el-input-number
                v-model="state.day.cronDaysNearestWeekday"
                :min="1"
                :max="31"
              ></el-input-number>
              {{ state.text.Day.nearestWeekday[1] }}
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.day.cronEvery" label="11"
              >{{ state.text.Day.someWeekday[0] }}
              <el-input-number
                v-model="state.week.cronNthDayNth"
                :min="1"
                :max="5"
              ></el-input-number>
              <el-select v-model="state.week.cronNthDayDay">
                <el-option
                  v-for="(val, index) in 7"
                  :key="index"
                  :label="state.text.Week[val - 1]"
                  :value="val"
                ></el-option>
              </el-select>
              {{ state.text.Day.someWeekday[1] }}
            </el-radio>
          </el-row>
        </div>
      </el-tab-pane>
      <el-tab-pane>
        <template #label>
          <span>
            {{ state.text.Month.name }}
          </span>
        </template>
        <div class="tabBody" :style="{ 'max-height': maxHeight }">
          <el-row>
            <el-radio v-model="state.month.cronEvery" label="1">{{
              state.text.Month.every
            }}</el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.month.cronEvery" label="2"
              >{{ state.text.Month.interval[0] }}
              <el-input-number
                v-model="state.month.incrementIncrement"
                :min="0"
                :max="12"
              ></el-input-number>
              {{ state.text.Month.interval[1] }}
              <el-input-number
                v-model="state.month.incrementStart"
                :min="0"
                :max="12"
              ></el-input-number>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio class="long" v-model="state.month.cronEvery" label="3"
              >{{ state.text.Month.specific }}
              <el-select multiple v-model="state.month.specificSpecific">
                <el-option
                  v-for="(val, index) in 12"
                  :key="index"
                  :label="val"
                  :value="val"
                ></el-option>
              </el-select>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.month.cronEvery" label="4"
              >{{ state.text.Month.cycle[0] }}
              <el-input-number
                v-model="state.month.rangeStart"
                :min="1"
                :max="12"
              ></el-input-number>
              {{ state.text.Month.cycle[1] }}
              <el-input-number v-model="state.month.rangeEnd" :min="1" :max="12"></el-input-number>
            </el-radio>
          </el-row>
        </div>
      </el-tab-pane>
      <el-tab-pane>
        <template #label>
          <span> {{ state.text.Year.name }}</span>
        </template>
        <div class="tabBody" :style="{ 'max-height': maxHeight }">
          <el-row>
            <el-radio v-model="state.year.cronEvery" label="1">{{
              state.text.Year.every
            }}</el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.year.cronEvery" label="2"
              >{{ state.text.Year.interval[0] }}
              <el-input-number
                v-model="state.year.incrementIncrement"
                :min="1"
                :max="99"
              ></el-input-number>
              {{ state.text.Year.interval[1] }}
              <el-input-number
                v-model="state.year.incrementStart"
                :min="2018"
                :max="2118"
              ></el-input-number>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio class="long" v-model="state.year.cronEvery" label="3"
              >{{ state.text.Year.specific }}
              <el-select filterable multiple v-model="state.year.specificSpecific">
                <el-option
                  v-for="(val, index) in 100"
                  :key="index"
                  :label="2017 + val"
                  :value="2017 + val"
                ></el-option>
              </el-select>
            </el-radio>
          </el-row>
          <el-row>
            <el-radio v-model="state.year.cronEvery" label="4"
              >{{ state.text.Year.cycle[0] }}
              <el-input-number
                v-model="state.year.rangeStart"
                :min="2018"
                :max="2118"
              ></el-input-number>
              {{ state.text.Year.cycle[1] }}
              <el-input-number
                v-model="state.year.rangeEnd"
                :min="2018"
                :max="2118"
              ></el-input-number>
            </el-radio>
          </el-row>
        </div>
      </el-tab-pane>
    </el-tabs>
    <div class="bottom">
      <div class="value">
        <p>
          <span class="tag-text">{{ state.text.Privew }}: </span>
          <el-tag>
            {{ state.cron }}
          </el-tag>
        </p>
        <p class="explain-box">
          <span class="explain-label">{{ state.text.Explain }}: </span>
          <el-tag :title="cronExplain">
            <span class="txt-ellipsis">{{ cronExplain }}</span>
          </el-tag>
        </p>
      </div>
      <div class="buttonDiv">
        <el-button type="primary" @click.stop="handleChange">{{ state.text.Save }}</el-button>
        <el-button type="primary" @click="close()">{{ state.text.Close }}</el-button>
      </div>
    </div>
  </div>
</template>
<style lang="scss" scoped>
.vue3-cron-div {
  .el-input-number__decrease,
  .el-input-number__increase {
    top: 2px !important;
  }
  .language {
    position: absolute;
    right: 25px;
    z-index: 1;
    top: 20px;
  }
  .el-tabs {
    box-shadow: none;
  }
  .tabBody {
    padding: 0 16px;
    overflow: auto;
    .el-row {
      margin: 20px 0;
      .long {
        .el-select {
          width: 350px;
        }
      }
      .el-input-number {
        width: 120px;
      }
    }
  }
  .bottom {
    width: 100%;
    margin-top: 5px;
    display: flex;
    align-items: center;
    justify-content: space-around;
    .value {
      float: left;
      font-size: 14px;
      vertical-align: middle;
      .explain-box {
        padding-top: 8px;
        display: flex;
        .explain-label {
          flex-shrink: 0;
        }
        .txt-ellipsis {
          max-width: 300px;
        }
      }
      .tag-text {
        color: red;
      }
    }
  }
}
</style>
